'''
展示了如何触发相机的操作，依赖于Enumeration，Acquisition，and NodemapInfo

'''

import PySpin

NUM_IMAGE = 2 #需要抓取的照片数

class TriggerType:
    SOFTWARE = 1
    HARDWARE = 2

CHOSEN_TRIGGER = TriggerType.SOFTWARE

def configure_trigger(cam):
    #第一步，选择触发源前要把触发模式关闭；第二部触发源选择后，设置触发模式可用，好像之后单张照片的执行模式
    result = True

    print(' *** 设置触发 *** ')
    if CHOSEN_TRIGGER == TriggerType.SOFTWARE:
        print('选择了软件触发模式')
    elif CHOSEN_TRIGGER == TriggerType.HARDWARE:
        print('选择了硬件触发模式')

    try:
        #1.关闭触发模式为off 2.设置触发源（硬件or软件） 3.打开触发模式为on
        #1。确保触发模式为off，
        #为了设置源是否为硬件或软件，必须让禁用触发器
        nodemap = cam.GetNodeMap()
        node_tigger_mode = PySpin.CEnumerationPtr(nodemap.GetNode('TriggerMode'))
        if not PySpin.IsAvailable(node_tigger_mode) or not PySpin.IsReadable(node_tigger_mode):
            print('不可设置触发模式')
            return False
        node_tigger_mode_off = node_tigger_mode.GetEntryByName('Off')
        if not PySpin.IsAvailable(node_tigger_mode_off) or not PySpin.IsReadable(node_tigger_mode_off):
            print('不可设置触发模式')
            return False
        node_tigger_mode.SetIntValue(node_tigger_mode_off.GetValue())

        print('已经禁用触发模式')

        #2.选择触发源
        #当设置触发源为硬件或者软件时，必须让触发模式为off
        node_tigger_source = PySpin.CEnumerationPtr(nodemap.GetNode('TriggerSource'))

        if not PySpin.IsAvailable(node_tigger_source) or not PySpin.IsWritable(node_tigger_source):
            print('无法设置触发源')
            return False

        if CHOSEN_TRIGGER == TriggerType.SOFTWARE:
            node_tigger_source_software = node_tigger_source.GetEntryByName('Software')
            if not PySpin.IsAvailable(node_tigger_source_software) or not PySpin.IsReadable(node_tigger_source_software):
                print('无法设置软件触发源')
                return False
            node_tigger_source.SetIntValue(node_tigger_source_software.GetValue())

        elif CHOSEN_TRIGGER == TriggerType.HARDWARE:
            node_tigger_source_hardware = node_tigger_source.GetEntryByName('Hardware')
            if not PySpin.IsAvailable(node_tigger_source_hardware) or not PySpin.IsReadable(
                    node_tigger_source_hardware):
                print('无法设置软件触发源')
                return False
            node_tigger_source.SetIntValue(node_tigger_source_hardware.GetValue())

        #3.触发模式为on
        node_tigger_mode_on = node_tigger_mode.GetEntryByName('On')
        if not PySpin.IsAvailable(node_tigger_mode_on) or not PySpin.IsReadable(node_tigger_mode_on):
            print('不可设置触发模式')
            return False
        node_tigger_mode.SetIntValue(node_tigger_mode_on.GetValue())
        print('触发模式又打开了')

    except PySpin.SpinnakerException as ex:
        print('Error %s' %ex)
        return False
    return result

def grab_next_image_by_trigger(nodemap, cam):
    '''
    这个函数通过执行触发来获取一张图片
    :param nodemap:设备的点图
    :param cam: 获取图片的相机
    :return: True or False
    '''
    try:
        result = True
        #使用触发抓取图片
        #软件触发，enter键模拟触发
        if CHOSEN_TRIGGER == TriggerType.SOFTWARE:
            #获取用户输入
            input('按enter键初始化软件触发器')

            #执行软件触发器
            node_softwaretrigger_cmd = PySpin.CCommandPtr(nodemap.GetNode('TriggerSoftware'))
            if not PySpin.IsAvailable(node_softwaretrigger_cmd) or not PySpin.IsWritable(node_softwaretrigger_cmd):
                print('不能执行触发')
                return False

            node_softwaretrigger_cmd.Execute()
        elif CHOSEN_TRIGGER == TriggerType.HARDWARE:
            print('使用硬件触发器')

    except PySpin.SpinnakerException as ex:
        print('Error:%s' %ex)
        return False
    return result





def acquire_image(cam, nodemap, nodemap_tldevice):
    print(' *** 图片获取 ***')
    try:
        result = True
        node_acquisition_mode = PySpin.CEnumerationPtr(nodemap.GetNode('AcquisitionMode'))
        if not PySpin.IsAvailable(node_acquisition_mode) or not PySpin.IsWritable(node_acquisition_mode):
            print('无法将采集模式设置为连续(enum检索)。流产……')  # 无法将获取模式设置为连续，异常终止
            return False

        # 从枚举节点检索入口节点
        node_acquisition_mode_continuous = node_acquisition_mode.GetEntryByName('Continuous')
        if not PySpin.IsAvailable(node_acquisition_mode_continuous) or not PySpin.IsReadable(
                node_acquisition_mode_continuous):
            print('无法将采集模式设置为连续(条目检索)。流产……')
            return False
        # 从入口节点检索整数值
        acquisition_mode_continuous = node_acquisition_mode_continuous.GetValue()
        # 设置条目节点的整数值为枚举节点的新值
        node_acquisition_mode.SetIntValue(acquisition_mode_continuous)

        print('获取模式设置为了连续（continuous）')

        #开始获取图片
        cam.BeginAcquisition()

        print('开始获取图像')

        # ****检索设备号，可有可无，方便定义文件名而已
        device_serial_number = ''
        node_device_serial_number = PySpin.CStringPtr(nodemap_tldevice.GetNode('DeviceSerialNumber'))
        if PySpin.IsAvailable(node_device_serial_number) and PySpin.IsReadable(node_device_serial_number):
            device_serial_number = node_device_serial_number.GetValue()
            print('设备序列号为%s...' % device_serial_number)

        #检索，转化，存储图片集
        for i in range(NUM_IMAGE):
            try:
                '''
                可以使用按enter接收一张图片，while里面的条件可以换成很多东西啦
                while(grab_next_image_by_trigger(nodemap, cam)):
                    image_result = cam.GetNextImage()
                    if image_result.IsIncomplete():
                        print('图片是不完整的，现在图片的状态是%d..' % image_result.GetImageStatus())
                    else:
                        width = image_result.GetWidth()
                        height = image_result.GetHeight()
                        print('抓取的图片%d，宽：%d，高：%d' % (i, width, height))
    
                            # 转化图片为单色8
                        image_converted = image_result.Convert(PySpin.PixelFormat_Mono8, PySpin.HQ_LINEAR)
    
                        if device_serial_number:
                            filename = "Trigger-%s-%d.jpg" % (device_serial_number, i)
                        else:  # if serial number is empty
                            filename = "Trigger-%d.jpg" % i
    
                        image_converted.Save(filename)
                        print('图像存储为%s' % filename)
    
                        # 释放图片，图片直接从相机中检索，为了让其缓存充足，需要释放缓存
                        image_result.Release()
                        print('')

                '''
                # 从触发器中检索下一张图片
                result &= grab_next_image_by_trigger(nodemap, cam)
                #检索下一个接收到的图片
                image_result = cam.GetNextImage()
                if image_result.IsIncomplete():
                    print('图片是不完整的，现在图片的状态是%d..' % image_result.GetImageStatus())
                else:
                    width = image_result.GetWidth()
                    height = image_result.GetHeight()
                    print('抓取的图片%d，宽：%d，高：%d' % (i, width, height))

                        # 转化图片为单色8
                    image_converted = image_result.Convert(PySpin.PixelFormat_Mono8, PySpin.HQ_LINEAR)

                    if device_serial_number:
                        filename = "Trigger-%s-%d.jpg" % (device_serial_number, i)
                    else:  # if serial number is empty
                        filename = "Trigger-%d.jpg" % i

                    image_converted.Save(filename)
                    print('图像存储为%s' % filename)

                    # 释放图片，图片直接从相机中检索，为了让其缓存充足，需要释放缓存
                    image_result.Release()
                    print('')

            except PySpin.SpinnakerException as ex:
                print('Error: %s' % ex)
                result = False

        # 结束获取,适当地结束收购有助于确保设备得到清理不需要功率循环来保持完整性。
        cam.EndAcquisition()


    except PySpin.SpinnakerException as ex:
        print('Error: %s' % ex)
        result = False

    return result

def reset_trigger(nodemap):
    #关闭触发，使相机回到正常状态
    try:
        result = True
        node_trigger_mode = PySpin.CEnumerationPtr(nodemap.GetNode("TriggerMode"))
        if not PySpin.IsAvailable(node_trigger_mode) or not PySpin.IsReadable(node_trigger_mode):
            print("无法设置触发模式")
            return False

        node_trigger_mode_off = node_trigger_mode.GetEntryByName("Off")
        if not PySpin.IsAvailable(node_trigger_mode_off) or not PySpin.IsReadable(node_trigger_mode_off):
            print("无法设置触发模式")
            return False

        node_trigger_mode.SetIntValue(node_trigger_mode_off.GetValue())

        print("已将触发模式关闭")

    except PySpin.SpinnakerException as ex:
        print("Error: %s" % ex)
        result = False

    return result


def run_single_camera(cam):
    try:
        result = True
        err = False

        #获取TL设备的点图，打印出设备信息
        nodemap_tldevice = cam.GetTLDeviceNodeMap()

        cam.Init()
        nodemap = cam.GetNodeMap()

        #设置触发
        if configure_trigger(cam) is False:
            return False

        #获取图片
        result = acquire_image(cam, nodemap, nodemap_tldevice)

        #重置触发
        result = reset_trigger(nodemap)

        #取消相机初始化
        cam.DeInit()

    except PySpin.SpinnakerException as ex:
        print('Error %s' %ex)
        result = False
    return False

def main():
    system= PySpin.System.GetInstance()

    cam_list = system.GetCameras()

    num_cam = cam_list.GetSize()

    print('检测到%d个相机' %num_cam)

    if num_cam == 0:
        #如果没有相机，就结束
        cam_list.Clear()
        system.ReleaseInstance()

        print('没有相机')

        input('输入Enter结束')
        return False

    for i in range(num_cam):
        cam = cam_list.GetByIndex(i)  #另一种获取相机的方法
        result = run_single_camera(cam)

    del cam
    cam_list.Clear()
    system.ReleaseInstance()

    input('按enter退出')
    return result


if __name__ == '__main__':
    main()
